// ==========================================================================
// gin-sns自动生成业务逻辑层相关代码，只生成一次，按需修改,再次生成不会覆盖.
// date：2021-02-08 21:07:30 +0800 CST
// path: app/service/module/message_service.go
// author：jon
// ==========================================================================
package module

import (
	"errors"
	"gin-sns/app/common"
	"gin-sns/app/common/email"
	"gin-sns/app/common/urls"
	"sync"

	"gin-sns/app/dao/module/article"
	"gin-sns/app/dao/module/comment"
	"gin-sns/app/dao/module/message"
	"gin-sns/app/dao/module/post"

	"gin-sns/app/model"
	"gin-sns/app/model/constants"

	commentModel "gin-sns/app/model/module/comment"
	messageModel "gin-sns/app/model/module/message"
	userModel "gin-sns/app/model/module/user"

	"log"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/mlogclub/simple/json"
	"github.com/sirupsen/logrus"
	"github.com/tidwall/gjson"

	"gin-sns/app/ginframe/cfg"
	"gin-sns/app/ginframe/tools"
	"gin-sns/app/ginframe/utils/convert"
	"gin-sns/app/ginframe/utils/page"
)

//todo
var messagesChan = make(chan *messageModel.Entity)
var messagesConsumeOnce sync.Once

var MessageService = newMessageService()

func newMessageService() *messageService {
	return &messageService{}
}

type messageService struct {
}

//根据主键查询数据
func (s *messageService) SelectRecordById(id int64) (*messageModel.Entity, error) {
	return message.MessageDao.SelectById(id)
}

//
func (s *messageService) SelectRecordsIn(idarr []int64) ([]messageModel.Entity, error) {

	list, err := message.MessageDao.FindIn("id", idarr)
	if list != nil && len(list) > 0 {
		err = errors.New("not found")
		list = nil
	}
	return list, err
}

//根据主键删除数据
func (s *messageService) DeleteRecordById(id int64) bool {
	return message.MessageDao.DeleteById(id)
}

//批量删除数据记录
func (s *messageService) DeleteRecordByIds(ids string) int64 {
	ida := convert.ToInt64Array(ids, ",")
	result, err := message.MessageDao.DeleteBatch(ida...)
	if err != nil {
		return 0
	}
	return result
}

//添加数据
func (s *messageService) AddSave(req *messageModel.AddReq, ctx *gin.Context) (int64, error) {
	var entity messageModel.Entity

	//entity.Id = req.Id
	entity.FromId = req.FromId
	entity.UserId = req.UserId
	entity.Title = req.Title
	entity.Content = req.Content
	entity.QuoteContent = req.QuoteContent
	entity.Type = req.Type
	entity.ExtraData = req.ExtraData
	entity.Status = req.Status
	entity.CreateTime = time.Now()
	//entity.CreateBy = ""

	//user := system.UserService.GetProfile(ctx)

	//if user != nil {
	//	entity.CreateBy = user.LoginName
	//}

	_, err := message.MessageDao.Insert(&entity)
	return entity.Id, err
}

//修改数据
func (s *messageService) EditSave(req *messageModel.EditReq, ctx *gin.Context) (int64, error) {
	entity := &messageModel.Entity{Id: req.Id}
	ok, err := message.MessageDao.FindOne(entity)

	if err != nil {
		return 0, err
	}

	if !ok {
		return 0, errors.New("数据不存在")
	}

	entity.FromId = req.FromId
	entity.UserId = req.UserId
	entity.Title = req.Title
	entity.Content = req.Content
	entity.QuoteContent = req.QuoteContent
	entity.Type = req.Type
	entity.ExtraData = req.ExtraData
	entity.Status = req.Status
	//entity.UpdateTime = time.Now()
	//entity.UpdateBy = ""

	//user := system.UserService.GetProfile(ctx)

	//if user == nil {
	//	entity.UpdateBy = user.LoginName
	//}

	return message.MessageDao.Update(entity)
}

//根据条件查询数据
func (s *messageService) SelectListAll(params *messageModel.SelectPageReq) ([]messageModel.Entity, error) {
	return message.MessageDao.SelectListAll(params)
}

//根据条件分页查询数据
func (s *messageService) SelectListByPage(params *messageModel.SelectPageReq) ([]messageModel.Entity, *page.Paging, error) {
	return message.MessageDao.SelectListByPage(params)
}

// 导出excel
func (s *messageService) Export(param *messageModel.SelectPageReq) (string, error) {
	head := []string{"", "FROM用户id", "用户id", "标题", "内容", "引用内容", "类型", "扩展数据", "0未审核 1正常状态 2审核未通过 3删除", "创建时间", "更新时间"}
	col := []string{"id", "from_id", "user_id", "title", "content", "quote_content", "type", "extra_data", "status", "create_time", "update_time"}
	return message.MessageDao.SelectListExport(param, head, col)
}

func (s *messageService) GetUnReadCount(userId int64) (int64, error) {
	return message.MessageDao.GetUnReadCount(userId)
}

// 读消息
func (s *messageService) Read(id int64) *messageModel.Entity {
	msg, _ := s.SelectRecordById(id)
	if msg != nil && msg.Status == constants.MsgStatusUnread {
		msg.Status = constants.MsgStatusReaded
		message.MessageDao.Update(msg) // 标记为已读
	}
	return msg
}

// 将所有消息标记为已读
func (s *messageService) MarkReadAll(userId int64) {
	message.MessageDao.MarkReadAll(userId)
}

//todo

func (s *messageService) SendCommentLikeMsg(commentId, likeUserId int64) {
	comment, _ := comment.CommentDao.SelectById(commentId)
	if comment == nil {
		return
	}
	if comment.UserId == likeUserId {
		return
	}
	var (
		title        = "点赞了你的话题"
		quoteContent = "《" + tools.GetSummary(comment.Content, 30) + "》"
	)
	s.Produce(likeUserId, comment.UserId, title, "", quoteContent, constants.MsgTypePostLike, map[string]interface{}{
		"commentId":  commentId,
		"likeUserId": likeUserId,
	})
}

// 话题收到点赞
func (s *messageService) SendPostLikeMsg(postId, likeUserId int64) {
	post, _ := post.PostDao.SelectById(postId)
	if post == nil {
		return
	}
	if post.UserId == likeUserId {
		return
	}
	var (
		title        = "点赞了你的话题"
		quoteContent = "《" + post.Title + "》"
	)
	s.Produce(likeUserId, post.UserId, title, "", quoteContent, constants.MsgTypePostLike, map[string]interface{}{
		"postId":     postId,
		"likeUserId": likeUserId,
	})
}

// 话题被收藏
func (s *messageService) SendArticleFavoriteMsg(articleId, favoriteUserId int64) {
	article, _ := article.ArticleDao.SelectById(articleId)
	if article == nil {
		return
	}
	if article.UserId == favoriteUserId {
		return
	}
	var (
		title        = "收藏了你的话题"
		quoteContent = "《" + article.Title + "》"
	)
	s.Produce(favoriteUserId, article.UserId, title, "", quoteContent, constants.MsgTypePostFavorite, map[string]interface{}{
		"articleId":      article,
		"favoriteUserId": favoriteUserId,
	})
}

// 话题被收藏
func (s *messageService) SendPostFavoriteMsg(postId, favoriteUserId int64) {
	post, _ := post.PostDao.SelectById(postId)
	if post == nil {
		return
	}
	if post.UserId == favoriteUserId {
		return
	}
	var (
		title        = "收藏了你的话题"
		quoteContent = "《" + post.Title + "》"
	)
	s.Produce(favoriteUserId, post.UserId, title, "", quoteContent, constants.MsgTypePostFavorite, map[string]interface{}{
		"postId":         postId,
		"favoriteUserId": favoriteUserId,
	})
}

// 话题被设为推荐
func (s *messageService) SendPostRecommendMsg(postId int64) {
	post, _ := post.PostDao.SelectById(postId)
	if post == nil {
		return
	}
	var (
		title        = "你的话题被设为推荐"
		quoteContent = "《" + post.Title + "》"
	)
	s.Produce(0, post.UserId, title, "", quoteContent, constants.MsgTypePostRecommend, map[string]interface{}{
		"postId": postId,
	})
}

// 话题被删除消息
func (s *messageService) SendPostDeleteMsg(postId, deleteUserId int64) {
	post, _ := post.PostDao.SelectById(postId)
	if post == nil {
		return
	}
	if post.UserId == deleteUserId {
		return
	}
	var (
		title        = "你的话题被删除"
		quoteContent = "《" + post.Title + "》"
	)
	s.Produce(0, post.UserId, title, "", quoteContent, constants.MsgTypePostDelete, map[string]interface{}{
		"postId":       postId,
		"deleteUserId": deleteUserId,
	})
}

// 评论被回复消息
func (s *messageService) SendCommentMsg(entity *commentModel.Entity) {
	var (
		fromId       = entity.UserId                                                // 消息发送人
		toId         int64                                                          // 消息接收人
		title        string                                                         // 消息的标题
		content      = common.GetSummary(constants.ContentTypeText, entity.Content) // 消息内容 //md //text
		quoteContent string                                                         // 引用内容
	)

	if entity.EntityType == constants.EntityTypeArticle { // 文章被评论
		article, _ := article.ArticleDao.SelectById(entity.EntityId)
		if article != nil {
			toId = article.UserId
			title = "回复了你的文章"
			quoteContent = "《" + article.Title + "》"
		}
	} else if entity.EntityType == constants.EntityTypePost { // 话题被评论
		post, _ := article.ArticleDao.SelectById(entity.EntityId)
		if post != nil {
			toId = post.UserId
			title = "回复了你的话题"
			quoteContent = "《" + post.Title + "》"
		}
	} else if entity.EntityType == constants.EntityTypeComment { // 话题被评论
		c, _ := comment.CommentDao.SelectById(entity.EntityId)
		if c != nil {
			toId = c.UserId
			title = "回复了你的评论"
			quoteContent = "《" + common.GetSummary(constants.ContentTypeText, c.Content) + "》"
		}
	}

	if toId <= 0 {
		return
	}

	quote := s.getQuoteComment(entity.QuoteId)
	if quote != nil { // 回复跟帖
		// 回复人和帖子作者不是同一个人，并且引用的用户不是帖子作者，需要给帖子作者也发送一下消息
		if fromId != toId && quote.UserId != toId {
			// 给帖子作者发消息（收到话题评论）
			s.Produce(fromId, toId, title, content, quoteContent, constants.MsgTypePostComment,
				map[string]interface{}{
					"entityType": entity.EntityType,
					"entityId":   entity.EntityId,
					"commentId":  entity.Id,
					"quoteId":    entity.QuoteId,
				})
		}

		// 给被引用的人发消息（收到他人回复）
		if fromId != quote.UserId {
			s.Produce(fromId, quote.UserId, "回复了你的评论", content, common.GetMarkdownSummary(quote.Content),
				constants.MsgTypeCommentReply, map[string]interface{}{
					"entityType": entity.EntityType,
					"entityId":   entity.EntityId,
					"commentId":  entity.Id,
					"quoteId":    entity.QuoteId,
				})
		}
	} else if fromId != toId { // 回复主贴，并且不是自己回复自己
		// 给帖子作者发消息（收到话题评论）
		s.Produce(fromId, toId, title, content, quoteContent, constants.MsgTypePostComment,
			map[string]interface{}{
				"entityType": entity.EntityType,
				"entityId":   entity.EntityId,
				"commentId":  entity.Id,
				"quoteId":    entity.QuoteId,
			})
	}
}

func (s *messageService) getQuoteComment(quoteId int64) *commentModel.Entity {
	if quoteId <= 0 {
		return nil
	}
	entity := &commentModel.Entity{Id: quoteId}
	_, err := comment.CommentDao.Insert(entity)
	if err != nil {
		return nil
	}

	return entity
}

// 生产，将消息数据放入chan
func (s *messageService) Produce(fromId, toId int64, title, content, quoteContent string, msgType int, extraDataMap map[string]interface{}) {
	s.Consume()

	to, _ := UserService.SelectRecordById(toId)
	if to == nil || to.Type != constants.UserTypeNormal {
		return
	}

	var (
		extraData string
		err       error
	)

	if extraData, err = json.ToStr(extraDataMap); err != nil {
		log.Println("格式化extraData错误", err)
	}
	messagesChan <- &messageModel.Entity{
		FromId:       fromId,
		UserId:       toId,
		Title:        title,
		Content:      content,
		QuoteContent: quoteContent,
		Type:         msgType,
		ExtraData:    extraData,
		Status:       constants.MsgStatusUnread,
		CreateTime:   time.Now(),
	}
}

// 消费，消费chan中的消息
func (s *messageService) Consume() {
	messagesConsumeOnce.Do(func() {
		go func() {
			log.Println("开始消费系统消息...")
			for {
				msg := <-messagesChan
				log.Println("处理消息：from=", msg.FromId, " to=", msg.UserId)

				if _, err := message.MessageDao.Insert(msg); err != nil {
					log.Println("创建消息发生异常...", err)
				} else {
					s.SendEmailNotice(msg)
				}
			}
		}()
	})
}

// 发送邮件通知
func (s *messageService) SendEmailNotice(msg *messageModel.Entity) {
	config := cfg.Instance()
	user, _ := UserService.SelectRecordById(msg.UserId)
	if user != nil && len(user.UserEmail) > 0 {
		var (
			siteTitle  = config.AppConf.SiteTitle
			emailTitle = siteTitle + " - 新消息提醒"
		)

		if msg.Type == constants.MsgTypePostComment {
			emailTitle = siteTitle + " - 收到话题评论"
		} else if msg.Type == constants.MsgTypeCommentReply {
			emailTitle = siteTitle + " - 收到他人回复"
		} else if msg.Type == constants.MsgTypePostLike {
			emailTitle = siteTitle + " - 收到点赞"
		} else if msg.Type == constants.MsgTypePostFavorite {
			emailTitle = siteTitle + " - 话题被收藏"
		} else if msg.Type == constants.MsgTypePostRecommend {
			emailTitle = siteTitle + " - 话题被设为推荐"
		} else if msg.Type == constants.MsgTypePostDelete {
			emailTitle = siteTitle + " - 话题被删除"
		}

		var from *userModel.Entity
		if msg.FromId > 0 {
			from, _ = UserService.SelectRecordById(msg.FromId)
		}
		err := email.SendTemplateEmail(from, user.UserEmail, emailTitle, emailTitle, msg.Content,
			msg.QuoteContent, &model.ActionLink{
				Title: "点击查看详情",
				Url:   urls.AbsUrl("/user/messages"),
			})
		if err != nil {
			logrus.Error(err)
		}
	} else {
		log.Println("邮件未发送，没设置邮箱...")
	}
}

// 查看消息详情链接地址
func (s *messageService) GetMessageDetailUrl(msg *messageModel.Entity) string {
	if msg.Type == constants.MsgTypePostComment ||
		msg.Type == constants.MsgTypeCommentReply {
		entityType := gjson.Get(msg.ExtraData, "entityType")
		entityId := gjson.Get(msg.ExtraData, "entityId")

		if entityType.Int() == constants.EntityTypeArticle {
			return urls.ArticleUrl(entityId.Int())
		} else if entityType.Int() == constants.EntityTypePost {
			return urls.PostUrl(entityId.Int())
		}
	} else if msg.Type == constants.MsgTypePostLike ||
		msg.Type == constants.MsgTypePostFavorite ||
		msg.Type == constants.MsgTypePostRecommend {
		postId := gjson.Get(msg.ExtraData, "postId")
		if postId.Exists() && postId.Int() > 0 {
			return urls.PostUrl(postId.Int())
		}
	}
	return urls.AbsUrl("/user/messages")
}
